#include "config.h"

#include <stdbool.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "dbg.h"
#include "list.h"
#include "sysutil.h"
#include "lsv_lib.h"

#include "lsv_wbuffer.h"

#define NS_PER_SEC 1000000

/** @file sliding window protocol
 *
 */

int lsv_wbuf_qos_init(lsv_wbuf_qos_t *qos, int64_t interval) {
        _gettimeofday(&qos->t1, NULL);
        _gettimeofday(&qos->t2, NULL);

        qos->interval = interval;

        qos->in_pc = 0;
        qos->in_pc2 = 0;
        qos->in_flow = 0.0;

        qos->out_pc = 0;
        qos->out_pc2 = 0;
        qos->out_flow = 0.0;

        qos->total_pages = 0;

        return 0;
}

int lsv_wbuf_qos_wait(lsv_wbuf_qos_t *qos, int watermark, int in, int64_t *delay) {
        *delay = 0;

        if (watermark - WBUF_CHUNK_NUM/3 <= 0) {
                return 0;
        }

        /* 距离函数：用乘除法，几何级数，不用加减法 */
        double last_factor = pow(1.1, watermark - WBUF_CHUNK_NUM/3);

        DINFO("wlc %u f %0.2f\n", watermark, last_factor);

        /*
         * iops2 = 1 / (1/iops1 + delay)
         * Q = iops * size
         * delay = size * (1/Q2 - 1/Q1)
         */
        double q_goal = qos->out_flow / last_factor;
        if (qos->in_flow > 0.0 && q_goal > 0.0) {
                *delay = (int64_t) (NS_PER_SEC * in * (1.0 / q_goal - 1.0 / qos->in_flow));

                DINFO("delay %lld = %u*%llu*(1.0/%0.2f-1.0/%0.2f) out %0.2f f %0.2f %u\n",
                       (long long) *delay,
                       NS_PER_SEC,
                       (LLU) in,
                       q_goal,
                       qos->in_flow,
                       qos->out_flow,
                       last_factor,
                       watermark);

                if (*delay > NS_PER_SEC / 10) {
                        *delay = NS_PER_SEC / 10;
                }
        } else {
                DINFO("in %0.2f out %0.2f\n", qos->in_flow, qos->out_flow);
        }

        return 0;
}

int lsv_wbuf_qos_update(lsv_wbuf_qos_t *qos, int in, int out) {
        DINFO_NOP("in %llu out %llu\n", (LLU)in, (LLU)out);

        _gettimeofday(&qos->t2, NULL);
        qos->in_pc2 += in;
        qos->out_pc2 += out;

        int64_t used = _time_used(&qos->t1, &qos->t2);
        if (used >= qos->interval) {
                int64_t in_pages = (int64_t)(qos->in_pc2 - qos->in_pc);
                int64_t out_pages = (int64_t)(qos->out_pc2 - qos->out_pc);
                DINFO_NOP("time %llu in %llu %llu out %llu %llu flow %lld %lld %lld total %lld\n",
                       (LLU)used,
                       (LLU)qos->in_pc,
                       (LLU)qos->in_pc2,
                       (LLU)qos->out_pc,
                       (LLU)qos->out_pc2,
                       (long long)in_pages,
                       (long long)out_pages,
                       (long long)(in_pages - out_pages),
                       (long long)qos->total_pages);
                qos->in_flow = 1.0 * NS_PER_SEC * in_pages / used;
                qos->out_flow = 1.0 * NS_PER_SEC * out_pages / used;

                qos->t1 = qos->t2;
                qos->in_pc = qos->in_pc2;
                qos->out_pc = qos->out_pc2;

                qos->total_pages += (in_pages - out_pages);
        }

        return 0;
}
